package whf.framework.tools.generator;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import whf.framework.util.Output;
import whf.framework.util.StringUtils;

/**
 * @author wanghaifeng
 *
 */
public abstract class AbstractGenerator {
	public static String LINE_SEPARATOR = System.getProperty("line.separator");

	private String packageName;

	private String className;

	private String superClassName;

	private String[] interfaces;

	private boolean isInterface;

	private boolean implementsSerializable;

	private List<Method> methods = new ArrayList<Method>();

	private List<Prop> propertis = new ArrayList<Prop>();

	private List<String> imports = new ArrayList<String>();

	public void setInterface(boolean b) {
		this.isInterface = b;
	}

	public void setInterfaces(String[] interfaces) {
		this.interfaces = interfaces;
	}

	public void setImplementsSerializable(boolean b) {
		this.implementsSerializable = b;
	}

	public void setSuperClassName(String clsName) {
		this.superClassName = clsName;
	}

	public void setClassName(String className) {
		this.className = className;
	}

	public void setPackageName(String packageName) {
		this.packageName = packageName;
	}

	public void addImport(String imp) {
		this.imports.add(imp);
	}

	public void addProp(String type, String name, boolean hasSetter,
			boolean hasGetter, String comment) {
		this.propertis.add(new Prop(type, name, hasSetter, hasGetter, comment));
	}

	public void outImports(Output out) throws IOException {
		for (int i = 0; imports != null && i < imports.size(); i++) {
			out.println(imports.get(i));
		}
	}

	public void addMethod(Method m) {
		this.methods.add(m);
	}

	public void addAbstractMethod(String name, String retType,
			String[] argTypes, String[] args, Class[] exceptions) {
		this.addMethod(new Method(name, retType, argTypes, args, exceptions));
	}

	public void addRealMethod(String name, String retType, String[] argTypes,
			String[] args, Class[] exceptions, String body) {
		this.addMethod(new Method(name, retType, argTypes, args, exceptions,
				body));
	}

	public void addMethod(String modifier, String name, String retType,
			String[] argTypes, String[] args, Class[] exceptions, String body) {
		Method m = new Method(name, retType, argTypes, args, exceptions, body);
		m.modifier = modifier;
		this.addMethod(m);
	}
	
	protected abstract void gen();

	public void output(Output out) throws IOException {
		this.gen();
		out.print("package").print(packageName).print(";");
		out.println();
		for (int i = 0; this.imports != null && i < this.imports.size(); i++) {
			out.println("import").print(imports.get(i)).print(";");
		}
		out.println();
		out.println("public").print(this.isInterface ? "interface" : "class")
				.print(this.className);
		if (!StringUtils.isEmpty(this.superClassName)) {
			out.println("\textends").print(this.superClassName);
		}
		if (this.interfaces != null && this.interfaces.length > 0) {
			out.println("\timplements").print(this.interfaces[0]);
			for (int i = 1; i < this.interfaces.length; i++) {
				out.print(",").print(this.interfaces[i]);
			}
		}
		out.print("{");
		out.println();
		if (this.implementsSerializable) {
			out.println("private static final long serialVersionUID = 0 ;")
					.println();
		}
		for (int i = 0; i < this.propertis.size(); i++) {
			Prop p = (Prop) this.propertis.get(i);
			out.println();
			if(!StringUtils.isEmpty(p.comment)){
				out.println("\t//").print(p.comment);
			}
			out.println("\tprivate").print(p.type).print(p.name).print(";");
		}
		for (int i = 0; i < this.propertis.size(); i++) {
			Prop p = (Prop) this.propertis.get(i);
			p.out(out);
		}
		out.println("//---------- methods ----------");
		for (int i = 0; i < this.methods.size(); i++) {
			Method m = (Method) this.methods.get(i);
			m.out(out);
		}
		out.println().println("}");
	}

	class Prop {
		private boolean hasSetter;

		private boolean hasGetter;

		private String type;

		private String name;
		
		private String comment;

		public Prop(String type, String name, boolean hasSetter,
				boolean hasGetter, String comment) {
			this.hasGetter = hasGetter;
			this.hasSetter = hasSetter;
			this.type = type;
			this.name = name;
			this.comment = comment;
		}

		public void out(Output out) throws IOException {
			if (hasSetter) {
				if(StringUtils.equalsIgnoreCase("boolean", type) && name.toLowerCase().startsWith("is")){
					String name2 = name.substring(2);
					Method m = new Method("set" + StringUtils.capitalize(name2),
							"void", new String[] { type }, new String[] { name2 },
							null, "this." + name + "=" + name2);
					addMethod(m);
				}else{
					Method m = new Method("set" + StringUtils.capitalize(name),
							"void", new String[] { type }, new String[] { name },
							null, "this." + name + "=" + name);
					addMethod(m);
				}
			}
			if (hasGetter) {
				if(StringUtils.equalsIgnoreCase("boolean", type)){
					String name2 = name.substring(2);
					if(name2.startsWith("is")){
						name2 = name2.substring(2);
					}
					Method m = new Method("is" + StringUtils.capitalize(name2),
							type, null, null, null, "return this." + name);
					addMethod(m);
				}else{
					Method m = new Method("get" + StringUtils.capitalize(name),
							type, null, null, null, "return this." + name);
					addMethod(m);
				}
			}
		}
	}

	class Method {
		private String modifier = "public";

		private String name;

		private String retType;

		private String[] argTypes;

		private String[] args;

		private Class[] exceptions;

		private String body;

		private boolean isAbstract = true;

		public Method(String name, String retType, String[] argTypes,
				String[] args, Class[] exceptions) {
			this.name = name;
			this.retType = retType;
			this.argTypes = argTypes;
			this.args = args;
			this.exceptions = exceptions;
			this.isAbstract = true;
		}

		public Method(String name, String retType, String[] argTypes,
				String[] args, Class[] exceptions, String body) {
			this.name = name;
			this.retType = retType;
			this.argTypes = argTypes;
			this.args = args;
			this.exceptions = exceptions;
			this.isAbstract = false;
			this.body = body;
		}

		public void out(Output out) throws IOException {
			out.println();
			out.println("\t").print(modifier).print(retType).print(name).print("(");
			for (int i = 0; argTypes != null && i < argTypes.length; i++) {
				String pName = "p" + argTypes[i];
				if (args != null && args.length > 0) {
					pName = args[i];
				}
				if (i > 0) {
					out.print(",");
				}
				out.print(argTypes[i]).print(pName);
			}
			out.print(")");
			for (int i = 0; exceptions != null && i < exceptions.length; i++) {
				if (i == 0) {
					out.print("throws").print(exceptions[0].getSimpleName());
				} else {
					out.print(",").print(exceptions[i].getSimpleName());
				}
			}
			if (this.isAbstract) {
				out.print(";");
			} else {
				out.print("{").println("\t\t" + body);
				if (!body.endsWith("}") && !body.endsWith(";"))
					out.print(";");
				out.println("\t}");
			}
		}
	}
}
